package com.cloud.gateway.filter;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

@Component
public class JwtSecurityFilter implements GlobalFilter, Ordered {

	@Value("${jwt.secret}")
	public String secret;

	private static final String UNAUTHORIZED = "{\"code\": \"401\",\"msg\": \"401 Unauthorized.\"}";
	private static final String LOGIN_URL = "/system/login"; // 登录
	private static final String LOGIN_COMPLETE = "/system/searchHumanName"; // 登录名补全
	private static final String LAWCASE_URL = "/lawcase/";
	private static final String INTERFACE_URL = "/interface/"; // 各子系统服务之间使用
	private static final String STATIC_URL = "/static/"; // 为第三方请求预留

	@Override
	public int getOrder() {
		return 1;
	}

	@Override
	public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
		ServerHttpRequest request = exchange.getRequest();
		String requestUrl = request.getPath().toString();
		if (LOGIN_URL.equals(requestUrl) || LOGIN_COMPLETE.equals(requestUrl) || requestUrl.indexOf(INTERFACE_URL) >= 0
				|| requestUrl.indexOf(STATIC_URL) >= 0 || requestUrl.indexOf(LAWCASE_URL) != -1) {
			return chain.filter(exchange);
		}
		String token = request.getHeaders().getFirst("Authorization");
		HumanSession info = parserClaims(token);
		if (StringUtils.isEmpty(token) || null == info) {
			ServerHttpResponse originalResponse = exchange.getResponse();
			originalResponse.setStatusCode(HttpStatus.UNAUTHORIZED);
			originalResponse.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
			DataBuffer buffer = originalResponse.bufferFactory().wrap(UNAUTHORIZED.getBytes(StandardCharsets.UTF_8));
			return originalResponse.writeWith(Flux.just(buffer));
		}
		// 请求头中添加当前信息
		String humanName = "", unitName = "";
		try {
			humanName = URLEncoder.encode(info.getHumanName(), "utf-8");
			unitName = URLEncoder.encode(info.getUnitName(), "utf-8");
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}
		ServerHttpRequest mutableReq = exchange.getRequest().mutate()
				.header(HumanSession.HUMAN_ID, info.getHumanId() + "").header(HumanSession.HUMAN_NAME, humanName)
				.header(HumanSession.UNIT_ID, info.getUnitId() + "").header(HumanSession.UNIT_NAME, unitName)
				.header(HumanSession.UNIT_CODE, info.getUnitCode())
				.header(HumanSession.ROLE_IDS, StringUtils.join(info.getRoleIds(), ",")).build();
		ServerWebExchange mutableExchange = exchange.mutate().request(mutableReq).build();
		return chain.filter(mutableExchange);

	}

	private HumanSession parserClaims(String token) {
		if (StringUtils.isEmpty(token))
			return null;
		HumanSession session = null;
		try {
			Claims claims = Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
			session = new HumanSession();
			Integer humanId = claims.get(HumanSession.HUMAN_ID, Integer.class);
			if (null != humanId) {
				session.setHumanId(Long.valueOf(humanId));
			}
			session.setHumanName(claims.get(HumanSession.HUMAN_NAME, String.class));
			Integer unitId = claims.get(HumanSession.UNIT_ID, Integer.class);
			if (null != unitId) {
				session.setUnitId(Long.valueOf(unitId));
			}
			session.setUnitName(claims.get(HumanSession.UNIT_NAME, String.class));
			session.setUnitCode(claims.get(HumanSession.UNIT_CODE, String.class));
			String roleIds = claims.get(HumanSession.ROLE_IDS, String.class);
			if (!StringUtils.isEmpty(roleIds)) {
				List<Long> rIds = new ArrayList<Long>();
				String[] ts = roleIds.split(",");
				for (String id : ts) {
					rIds.add(Long.valueOf(id));
				}
				session.setRoleIds(rIds);
			}

		} catch (Exception e) {
			throw new RuntimeException(e);
		}
		return session;
	}

}
